package org.jsequences;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Predicates.*;
import java.util.Collection;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;

/**
 * Fluent Wrapper for the Function-Class, which allows it to chain functions and predicates directly
 * in a fluent way, much like FluentIterable does it for Iterable. Chaining via FluentFunction will
 * nearly always result in better-to-read-code than using Functions/Predicates.compose, which have
 * a strange left-to-right syntax and get unreadable very quickly when more than one composition is
 * done.
 * <p>
 * <b>Implementation Note</b>: Currently, there is <b>no</b> equivalent <i>FluentPredicate</i>. The
 * reason behind this is that code analysis and tests have shown that such a class does not help
 * much. While chaining functions and predicates is used commonly and very important, most predicate
 * operations are better done via static imports. Just compare <code>somepredicate.not()</code> to
 * <code>not(somepredicate)</code>.
 * 
 * @param <F> input type
 * @param <T> output type
 * @author Eric Giese
 */
public abstract class FluentFunction<F, T> implements Function<F, T> {

    // See FluentIterable for an explanation of this pattern
    private final Function<F, T> function;

    /** Constructor for use by subclasses. */
    protected FluentFunction() {
        this.function = this;
    }

    FluentFunction(Function<F, T> function) {
        this.function = checkNotNull(function);
    }

    /**
     * Returns a fluent function that wraps {@code function}, or {@code function} itself if it
     * is already a {@code FluentFunction}.
     */
    public static <F, T> FluentFunction<F, T> from(final Function<F, T> function) {
        return function instanceof FluentFunction ? (FluentFunction<F, T>) function :
                new FluentFunction<F, T>(function) {

                    public T apply(F input) {
                        return function.apply(input);
                    }

                };
    }

    /**
     * Returns the composition of two functions. For {@code f: A->B} and {@code g: B->C},
     * composition is defined as the function h such that {@code h(a) == g(f(a))} for each {@code a}
     * .
     */
    public final <C> FluentFunction<F, C> andThen(Function<? super T, C> function) {
        return from(Functions.compose(function, this.function));
    }

    /**
     * Returns the composition of this function and a predicate.
     * For every {@code x}, the generated predicate returns {@code predicate(function(x))}.
     */
    public final Predicate<F> andThen(Predicate<? super T> predicate) {
        return compose(predicate, this.function);
    }

    /**
     * Returns the composition of this function and the negation of a predicate.
     * For every {@code x}, the generated predicate returns {@code not(predicate(function(x)))}.
     */
    public final Predicate<F> andThenNot(Predicate<? super T> predicate) {
        return andThen(not(predicate));
    }

    /**
     * Converts this function into a predicate which checks if the function result is equal to the
     * given value. The predicate evaluates to {@code true} if the object being
     * tested {@code equals()} the given target or both are null.
     * 
     * @see Predicates#equalTo(Object)
     */
    public final Predicate<F> isResult(Object value) {
        return andThen(equalTo(value));
    }

    /**
     * Converts this function into a predicate which checks if the function result is not equal to
     * the given value. The predicate evaluates to {@code true} if the object being
     * tested not {@code equals()} the given target or the target is not null.
     * 
     * @see Predicates#equalTo(Object)
     * @see Predicates#not(Predicate)
     */
    public final Predicate<F> isResultNot(T value) {
        if (value == null) {
            return andThen(notNull());
        }
        return andThen(not(equalTo(value)));
    }

    /**
     * Converts this function into a predicate which checks if the function result is contained in
     * the given collection. The predicate evaluates to {@code true} if the object reference
     * being tested is a member of the given collection. It does not defensively
     * copy the collection passed in, so future changes to it will alter the
     * behavior of the predicate.
     * 
     * @see Predicates#in(Collection)
     */
    public final Predicate<F> isResultIn(Collection<? extends T> target) {
        return andThen(in(target));
    }

    /**
     * Converts this function into a predicate which checks if the function result is NOT contained
     * in the given collection. The predicate evaluates to {@code true} if the object reference
     * being tested is NOT a member of the given collection. It does not defensively
     * copy the collection passed in, so future changes to it will alter the
     * behavior of the predicate.
     * 
     * @see Predicates#in(Collection)
     * @see Predicates#not(Predicate)
     */
    public final Predicate<F> isResultNotIn(Collection<? extends T> target) {
        return andThenNot(in(target));
    }
}
